/*
*Copyright (c) Nikhil Jain.
*Project ICARUS, communications and 
*artificial intelligence.
*/



/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
 */
package javadrone.server;

//~--- non-JDK imports --------------------------------------------------------

import javadrone.Drone;
import javadrone.Sensor;
import javadrone.SensoryData;
import javadrone.State;

import javadrone.server.listeners.*;

//~--- JDK imports ------------------------------------------------------------

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 *
 * @author Nikzzay
 */
public class StateImpl extends UnicastRemoteObject implements State {
    private ConcurrentHashMap<Sensor, SensoryData> sensedState = new ConcurrentHashMap<>();
    private StateChangeEventNotifier               notifier    = new StateChangeEventNotifier();
    private Drone                                  drone;

    public StateImpl(Drone drone) throws RemoteException {
        this.drone = drone;
    }

    @Override
    public void setStateData(SensoryData<?> data) throws RemoteException {
        sensedState.put(data.getDescriptor(), data);
        notifier.notifyStateChangeEvent(new StateChangeEvent(sensedState.get(data.getDescriptor()), data, drone));
    }

    public SensoryData getStateData(Sensor descriptor) {
        return sensedState.get(descriptor);
    }

    public void addNavigationStateChangeListener(NavigationStateChangeListener scl) {
        notifier.addNavigationListener(scl);
    }

    public void addBootStateChangeListener(BootStateChangeListener scl) {
        notifier.addBootListener(scl);
    }

    public void addErrorStateChangeListener(ErrorStateChangeListener scl) {
        notifier.addErrorStateChangeListener(scl);
    }

    public void addAerialStateChangeListener(AerialStateChangeListener scl) {
        notifier.addAerialStateChangeListener(scl);
    }

    public void addVideoStateChangeListener(VideoStateChangeListener vscl) {
        notifier.addVideoStateChangeListener(vscl);
    }

    public void addCollisionStateChangeListener(CollisionStateChangeListener cscl) {
        notifier.addCollisionStateChangeListener(cscl);
    }

    public void addStateChangeListener(StateChangeListener scl) {
        notifier.addStateChangeListener(scl);
    }

    public void shutDown() {
        notifier.shutDown();
    }

    @Override
    public void finalize() throws Throwable {
        super.finalize();
        shutDown();
    }

    public class StateChangeEventNotifier implements Runnable {
        private List<StateChangeListener>          navigationListenerList     = new ArrayList<>();
        private List<StateChangeListener>          errorListenerList          = new ArrayList<>();
        private List<StateChangeListener>          bootListenerList           = new ArrayList<>();
        private List<StateChangeListener>          aerialStateListenerList    = new ArrayList<>();
        private List<VideoStateChangeListener>     videoStateListenerList     = new ArrayList<>();
        private List<StateChangeListener>          stateChangeListenerList    = new ArrayList<>();
        private ExecutorService                    pool                       = Executors.newCachedThreadPool();
        private List<CollisionStateChangeListener> collisionStateListenerList = new ArrayList<>();
        private StateChangeEvent                   sce;

        public StateChangeEventNotifier() {}

        public void shutDown() {
            pool.shutdownNow();
        }

        public void addNavigationListener(NavigationStateChangeListener scl) {
            navigationListenerList.add(scl);
        }

        public void addBootListener(BootStateChangeListener scl) {
            bootListenerList.add(scl);
        }

        public void addErrorStateChangeListener(ErrorStateChangeListener scl) {
            errorListenerList.add(scl);
        }

        public void addAerialStateChangeListener(AerialStateChangeListener scl) {
            aerialStateListenerList.add(scl);
        }

        public void addVideoStateChangeListener(VideoStateChangeListener vscl) {
            videoStateListenerList.add(vscl);
        }

        public void addCollisionStateChangeListener(CollisionStateChangeListener cscl) {
            collisionStateListenerList.add(cscl);
        }

        public void addStateChangeListener(StateChangeListener scl) {
            stateChangeListenerList.add(scl);
        }

        public Future notifyStateChangeEvent(StateChangeEvent<? extends SensoryData> sce) {
            this.sce = sce;

            return pool.submit(this);
        }

        @Override
        public void run() {
            Iterator listIterator;

            switch (sce.getDescriptor()) {
            case NAV_STATE :
                listIterator = navigationListenerList.iterator();

                break;

            case BOOT_STATE :
                listIterator = bootListenerList.iterator();

                break;

            case ERROR_STATE :
                listIterator = errorListenerList.iterator();

                break;

            case AERIAL_STATE :
                listIterator = aerialStateListenerList.iterator();

                break;

            case VID_STATE :
                listIterator = videoStateListenerList.iterator();

                break;

            default :
                listIterator = null;
            }

            while ((listIterator != null) && listIterator.hasNext()) {
                ((StateChangeListener) listIterator.next()).stateChanged(sce);
            }

            listIterator = stateChangeListenerList.iterator();

            while ((listIterator != null) && listIterator.hasNext()) {
                ((StateChangeListener) listIterator.next()).stateChanged(sce);
            }
        }
    }
}


//~ Formatted by Jindent --- http://www.jindent.com
